/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package generadorabm;

import generadorcommun.Atributo;
import StrategyClass.Abstracta;
import StrategyClass.ClaseC;
import StrategyClass.Interface;
import StrategyClass.StrategyClase;
import StrategyFuncion.AbstractaF;
import StrategyFuncion.Funcion;
import StrategyFuncion.Geter;
import StrategyFuncion.InterfaceF;
import StrategyFuncion.StrategyFuncion;
import StrategyProcedimiento.AbstractaP;
import StrategyProcedimiento.InterfaceP;
import StrategyProcedimiento.Prosedimiento;
import StrategyProcedimiento.Seter;
import StrategyProcedimiento.StrategyProcedimiento;
import generadorcommun.CrearPersistencia;
import generadorcommun.DataBase;
import generadorcommun.EnumClase;
import generadorcommun.EnumMetodo;
import generadorcommun.GuardarArchivo;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 *
 * @author Martin
 */
public class CreadoraClase {

    private String nombreProyecto;
    private Clase clase = new Clase();
    private List<Atributo> atributos = new LinkedList<Atributo>();
    private List<Metodo> metodos = new LinkedList<Metodo>();
    private List<Metodo> geters_Y_Seters = new LinkedList<Metodo>();
    private StrategyClase stClase;
    private StrategyFuncion stFuncion;
    private StrategyProcedimiento stProcedimiento;

    public CreadoraClase(String nombre) {
        nombreProyecto = nombre;

    }

    public List<Metodo> getGeters_Y_Seters() {
        return geters_Y_Seters;
    }

    public void lipiargetGeters_Y_Seters(List<Metodo> metodos) {
        this.metodos = metodos;
    }

    public void setGeters_Y_Seters(List<Metodo> geters_Y_Seters) {
        this.geters_Y_Seters = geters_Y_Seters;
    }

    public String getNombreProyecto() {
        return nombreProyecto;
    }

    public void setNombreProyecto(String nombreProyecto) {
        this.nombreProyecto = nombreProyecto;
    }

    public List<Atributo> getAtributos() {
        return atributos;
    }

    public void lipiarAtributos(List<Atributo> atributos) {
        this.atributos = atributos;
    }

    public void setAtributos(Atributo atributo) {
        boolean b = true;
        for (Atributo a : atributos) {
            if (a.getNombre().equals(atributo.getNombre())) {
                b = false;
            }
        }
        if (b) {
            this.atributos.add(atributo);
            Controladora.getInstance().getAtributos().notificarAtributo();
        }
    }

    public Clase getClase() {
        return clase;
    }

    public void setClase(Clase clase) {
        this.clase = clase;
    }

    public List<Metodo> getMetodos() {
        return metodos;
    }

    public void lipiarMetodos(List<Metodo> metodos) {
        this.metodos = metodos;
    }

    public void setMetodos(Metodo metodo) {
        this.metodos.add(metodo);
        Controladora.getInstance().getMetodos().notificarMetodo();
    }

    public StrategyClase getStClase() {
        return stClase;
    }

    public void setStClase(Clase clase) {
        StrategyClase cl = null;
        if (clase.getTipoClase().equals("class")) {
            cl = new ClaseC();
        } else if (clase.getTipoClase().equals("abstract")) {
            cl = new Abstracta();
        }
        if (clase.getTipoClase().equals("interface")) {
            cl = new Interface();
        }
        this.stClase = cl;
    }

    public StrategyFuncion getStFuncion() {
        return stFuncion;
    }

    public void setStFuncion(Metodo met) {
        StrategyFuncion fun = null;
        if (met.getTipoMetodo().equals("")) {
            fun = new Funcion();
        } else if (met.getTipoMetodo().equals("abstract")) {
            fun = new AbstractaF();
        } else if (met.getTipoMetodo().equals(EnumMetodo.GETER.toString())) {
            fun = new Geter();
        }if(met.getTipoMetodo().equals("interface")){
            fun =  new InterfaceF();
        }
        this.stFuncion = fun;
    }

    public StrategyProcedimiento getStProcedimiento() {
        return stProcedimiento;
    }

    public void setStProcedimiento(Metodo met) {
        StrategyProcedimiento p = null;
        if (met.getTipoMetodo().equals(EnumMetodo.PROCES.toString())) {
            p = new Prosedimiento();
        } else if (met.getTipoMetodo().equals(EnumMetodo.ABSTRACT.toString())) {
            p = new AbstractaP();

        } else if (met.getTipoMetodo().equals(EnumMetodo.SETER.toString())) {
            p = new Seter();
        }if(met.getTipoMetodo().equals("interface")){
            p =  new InterfaceP();
        }
        this.stProcedimiento = p;
    }

    public void generarClase(List<String> claseG, String nombre) {
        setStClase(clase);
        getStClase().darClase(claseG, clase, nombre);
    }

    public File darDirSingleton(int i) {
        File dir = null;
        switch (i) {
            case 1:
                dir = new File("TXTGENERADOR/singleton_1.txt");
                break;
            case 2:
                dir = new File("TXTGENERADOR/singleton_2.txt");
                break;
            case 3:
                dir = new File("TXTGENERADOR/singleton_3.txt");
                break;
            case 4:
                dir = new File("TXTGENERADOR/singleton_4.txt");
                break;
            case 5:
                dir = new File("TXTGENERADOR/singleton_5.txt");
                break;
            case 6:
                dir = new File("TXTGENERADOR/singleton_6.txt");
                break;
        }
        return dir;
    }

    public void concatenarList(List<String> principal, List<String> secundario) {
        for (String linea : secundario) {
            principal.add(principal.size() - 1, linea);
        }
    }

    public String sustituir(String texto, String sustituir) {
        String aux = "";
        for (char c : texto.toCharArray()) {
            if (c == '%') {
                aux = aux + sustituir;
            } else {
                aux = aux + c;
            }
        }
        return aux;
    }

    public void generarSingleton(List<String> clase) throws IOException {
        for (int i = 1; i <= 6; i++) {
            if (i == 4 || i == 6) {
                concatenarList(clase, GuardarArchivo.getInstance().leerTxt(darDirSingleton(i)));
            } else {
                clase.add(clase.size() - 1, sustituir(GuardarArchivo.getInstance().leerTxt(darDirSingleton(i)).get(0), this.clase.getNombre()));
            }
        }
    }

    public void generarAtributos(List<String> clase) {
        for (Atributo atributo : atributos) {
            clase.add(clase.size() - 1, atributo.getTipoAtributo() + " " + atributo.getTipo() + " " + atributo.getNombre() + ";");
            Metodo seter = new Metodo();
            seter.setNombre(atributo.getNombre());
            seter.setTipo(atributo.getTipo());
            seter.setTipoRetorno("");
            seter.setTipoMetodo(EnumMetodo.SETER.toString());
            Parametro p = new Parametro();
            p.setNombre(atributo.getNombre());
            p.setTipo(atributo.getTipo());
            seter.setParametros(p);
            Metodo geter = new Metodo();
            geter.setNombre(atributo.getNombre());
            geter.setTipoRetorno(atributo.getTipo());
            geter.setTipoMetodo(EnumMetodo.GETER.toString());
            geters_Y_Seters.add(seter);
            geters_Y_Seters.add(geter);
        }
    }

    public void generarMetodos(List<String> clase, List<Metodo> metodos) {
        for (Metodo met : metodos) {
            if (met.getTipoRetorno().equals("")) {
                setStProcedimiento(met);
                getStProcedimiento().darMetodo(clase, met);
            } else {
                setStFuncion(met);
                getStFuncion().darMetodo(clase, met);
            }
        }
    }

    public boolean esTipoBasico(String tipo) {
        boolean res = false;
        String aux = tipo.toUpperCase();
        if (aux.equals("INTEGER")) {
            res = true;
        } else if (aux.equals("DOUBLE")) {
            res = true;
        } else if (aux.equals("CHAR")) {
            res = true;
        } else if (aux.equals("BOOLEAN")) {
            res = true;
        } else if (aux.equals("LONG")) {
            res = true;
        } else if (aux.equals("FLOAT")) {
            res = true;
        }
        return res;
    }

    public boolean tipoLista(String lista) {
        String tipo = "";
        int i = 0;
        for (char c : lista.toCharArray()) {
            i++;
            if (i <= 4) {
                tipo += "" + c;
            }
        }
        if (tipo.equals("LIST")) {
            return true;
        } else {
            return false;
        }
    }

    public boolean tipoBasico(String tipo) {
        boolean res = false;
        String aux = tipo.toUpperCase();
        if (aux.equals("INTEGER")) {
            res = true;
        } else if (aux.equals("DOUBLE")) {
            res = true;
        } else if (aux.equals("CHAR")) {
            res = true;
        } else if (aux.equals("BOOLEAN")) {
            res = true;
        } else if (aux.equals("LONG")) {
            res = true;
        } else if (aux.equals("FLOAT")) {
            res = true;
        }
        return res;
    }

    public String darTipoLista(String lista) {
        boolean aux = true;
        int i = 5;
        String tipoLista = "";
        while (aux) {
            if (lista.charAt(i) != '>') {
                tipoLista += lista.charAt(i);
                i++;
            } else {
                aux = false;
            }
        }
        return tipoLista;
    }

    public String agregarAtributo(String atributos, String nombre, String tipo) {
        String atributo = " " + nombre + " " + tipo.toUpperCase() + " NOT NULL ,";
        return atributos + atributo;
    }

    public void crearTabla(String nombreClase, String atributosTabla) throws SQLException {
        String query = "";
        String query1 = "CREATE TABLE " + nombreProyecto.toLowerCase() + "." + nombreClase + "(ID INTEGER NOT NULL AUTO_INCREMENT,";
        String query2 = atributosTabla + "PRIMARY KEY (ID)) ENGINE = InnoDB ";
        query = query1 + query2;
        DataBase.getInstance().crearTabla(query, nombreProyecto);
    }

    public void agregarColumnaDataBase(String nomDB, String nombre, String tipo) throws SQLException {
        String query = "ALTER TABLE " + nombreProyecto + "." + nomDB + " ADD " + nombre + " " + tipo;
        DataBase.getInstance().crearTabla(query, nombreProyecto);
    }

    public boolean seRepite(String nom, List<String> lista) {
        boolean res = false;
        for (String aux : lista) {
            if (aux.equals(nom)) {
                res = true;
            }
        }
        return res;
    }

    public List<String> eliminarTiposRepetidos(List<String> fk) {
        String nom = "";
        boolean aux = true;
        List<String> fks = new ArrayList<String>();
        List<String> nombs = new ArrayList<String>();
        for (String linea : fk) {
            fks.add(linea);
        }
        for (int i = 0; i < fks.size() - 1; i++) {
            for (char c : fks.get(i).toCharArray()) {
                if (c == '|') {
                    aux = false;
                }
                if (aux) {
                    nom += c;
                }
            }
            nombs.add(nom);
            aux = true;
            nom = "";
        }
        int j = 0;
        while (j < fks.size() - 1) {
            if (seRepite(nom, nombs)) {
                nombs.remove(j);
                fks.remove(j);
            }
            j++;
        }
        return fks;
    }

    public void crearFK_DataBase(List<String> fk) throws SQLException {
        List<String> fks = eliminarTiposRepetidos(fk);
        boolean aux = false;
        String query = "";
        String nombreTabla = "";
        String nombre = "";
        String query1 = "ALTER TABLE " + nombreProyecto + ".";
        String query2 = " ADD CONSTRAINT FK_";
        String query3 = " FOREIGN KEY FK_";
        String query4 = " (ID_FK";
        String query5 = ") ";
        String query6 = " REFERENCES ";
        String query7 = "ON DELETE CASCADE ON UPDATE CASCADE ";
        for (String tabla_Nombre : fks) {
            query = "";
            nombreTabla = "";
            nombre = "";
            aux = false;
            for (char c : tabla_Nombre.toCharArray()) {
                if (c == '|') {
                    aux = true;
                }else if (aux) {
                    nombre += c;
                } else {
                    nombreTabla += c;
                }
            }
            query = query1 + nombreTabla + query2 + nombre + query3 + nombre + query4 + nombre + query5 + query6 + this.clase.getNombre() + " (ID) " + query7;
            DataBase.getInstance().crearTabla(query, nombreProyecto);
        }
    }

    public void procedimientoLista(List<Atributo> listAtributo, List<String> crearFK, Atributo a,List<Atributo> atributosRemover) throws SQLException {
        String tipoLista = darTipoLista(a.getTipo());
        if (tipoBasico(tipoLista)) {
            String atributoLista = " " + a.getNombre() + " " + tipoLista + " NOT NULL ,";
            crearTabla("list" + a.getNombre(), atributoLista);
            agregarColumnaDataBase("list" + a.getNombre(), "ID_FK" + a.getNombre(), "INTEGER");
            crearFK.add("list" + a.getNombre() + "|" + a.getNombre());
        } else if (tipoLista.equals("String")) {
            String atributoLista = " " + a.getNombre() + " VARCHAR(250) NOT NULL ,";
            crearTabla("list" + a.getNombre(), atributoLista);
            agregarColumnaDataBase("list" + a.getNombre(), "ID_FK" + a.getNombre(), "INTEGER");
            crearFK.add("list" + a.getNombre() + "|" + a.getNombre());
        } else {
            agregarColumnaDataBase(tipoLista, "ID_FK" + a.getNombre(), "INTEGER");
            crearFK.add(tipoLista + "|" +  a.getNombre());
        }
        a.setTipo(tipoLista);
        listAtributo.add(a);
        atributosRemover.add(a);
    }

    public String procedimientoAtributo(List<String> crearFK, Atributo a, String atributosTabla, List<Atributo> atributosNoBasicos,List<Atributo> atributosRemover) throws SQLException {
        if (!a.getNombre().equals("id")) {
            if (tipoBasico(a.getTipo())) {
                atributosTabla = agregarAtributo(atributosTabla, a.getNombre(), a.getTipo());
            } else if (a.getTipo().equals("String")) {
                atributosTabla = agregarAtributo(atributosTabla, a.getNombre(), "VARCHAR(250)");
            } else {
                agregarColumnaDataBase(a.getTipo(), "ID_FK" + a.getNombre(), "INTEGER");
                crearFK.add(a.getTipo() + "|" + a.getNombre());
                atributosNoBasicos.add(a);
                atributosRemover.add(a);
            }
        }
        return atributosTabla;
    }

    public void generarTabla() throws SQLException, IOException {
        String atributosTabla = "";
        List<String> crearFK = new LinkedList<String>();
        List<String> columnas = new ArrayList<String>();
        List<Atributo> listAtributo = new LinkedList<Atributo>();
        List<Atributo> atributosNoBasicos = new LinkedList<Atributo>();
        List<Atributo> atributos_a_Remover = new LinkedList<Atributo>();
        for (Atributo a : this.atributos) {
            if (tipoLista(a.getTipo().toUpperCase())) {
                procedimientoLista(listAtributo, crearFK, a,atributos_a_Remover);
            } else {
                atributosTabla = procedimientoAtributo(crearFK, a, atributosTabla, atributosNoBasicos,atributos_a_Remover);
            }
        }
        for (Atributo ar : atributos_a_Remover){
            this.atributos.remove(ar);
        }
        crearTabla(clase.getNombre(), atributosTabla);
        crearFK_DataBase(crearFK);
        CrearPersistencia.getInstance().generarPersistencia(clase.getNombre(), nombreProyecto, listAtributo, atributosNoBasicos, atributos);
    }

    public void generarCodigo(boolean singleton, boolean database) throws IOException, SQLException {
        List<String> codigoClase = new ArrayList<String>();
        generarClase(codigoClase, getNombreProyecto());
        if (singleton) {
            generarSingleton(codigoClase);
        }
        if (database) {
            Atributo a = new Atributo();
            a.setNombre("id");
            a.setTipo("Integer");
            a.setTipoAtributo("private");
            this.atributos.add(0, a);
        }
        generarAtributos(codigoClase);
        generarMetodos(codigoClase, geters_Y_Seters);
        generarMetodos(codigoClase, metodos);
        GuardarArchivo.getInstance().persistirClase(codigoClase, this.clase.getNombre(), nombreProyecto, this.clase.getTipoClase());
        if (database) {
            generarTabla();
        }
    }

    public void limpiar() {
        atributos = new LinkedList<Atributo>();
        metodos = new LinkedList<Metodo>();
        geters_Y_Seters = new LinkedList<Metodo>();
    }
}
